/*--------------------------------------------------------------------------
Modified from GNU Group's SDCC
Only for study purpose
---------------------------------------------------------------------------*/

#ifndef  __COMPILER_GROUP_SYMT_H
#define  __COMPILER_GROUP_SYMT_H

#include "Global.h"

#define MAX_NEST_LEVEL  256
#define MAX_SYMBOL_NAME 64
#define MAX_NAME  3*MAX_SYMBOL_NAME // big enough for _<func>_<var>_etc

#define TYPE_TARGET_CHAR  char
#define TYPE_TARGET_INT   short
#define TYPE_TARGET_LONG  int
#define TYPE_TARGET_UCHAR unsigned char
#define TYPE_TARGET_UINT  unsigned short
#define TYPE_TARGET_ULONG unsigned int

enum {
    TYPEOF_INT=1,
    TYPEOF_SHORT,
    TYPEOF_CHAR,
    TYPEOF_LONG,
    TYPEOF_VOID,
    TYPEOF_STRUCT,
    TYPEOF_ARRAY,
    TYPEOF_FUNCTION,
    TYPEOF_POINTER,
};

/* storage class    */
typedef enum
{
	S_FIXED = 0,
	S_AUTO,
	S_REGISTER,
	S_CODE,
	S_DATA,
	S_IDATA,
	S_LITERAL,
	S_STACK
} STORAGE_CLASS;

#define HASHTAB_SIZE 256

/* hash table bucket */
typedef struct bucket
{
	void *sym;                          /* pointer to the object      */
	char name[MAX_NAME + 1];       /* name of this symbol        */
	int level;                          /* nest level for this symbol */
	int block;                          /* belongs to which block     */
	struct bucket *prev;                /* ptr 2 previous bucket      */
	struct bucket *next;                /* ptr 2 next bucket          */
} bucket;

/* struct defination */
typedef struct structdef
{
	char tag[MAX_NAME + 1];        /* tag part of structure(name)*/
	unsigned char level;                /* Nesting level              */
	struct symbol *fields;              /* pointer to fields          */
	unsigned size;                      /* sizeof the table in bytes  */
	int type;                           /* STRUCT or UNION            */
} structdef;

/* noun definitions */
typedef enum
{
	V_INT = 1,
	V_CHAR,
	V_VOID,
	V_STRUCT,
	V_LABEL,
}
NOUN;

/* specifier is the last in the type-chain */
typedef struct specifier
{
	NOUN noun;                          /* CHAR INT STRUCTURE LABEL   */
	STORAGE_CLASS sclass;               /* REGISTER, AUTO, FIX, CONSTANT */
    struct memmap *oclass;              /* output storage class       */

	unsigned b_long:1;                  /* 1=long                     */
	unsigned b_short:1;                 /* 1=short int                */
	unsigned b_unsigned:1;              /* 1=unsigned, 0=signed       */
	unsigned b_signed:1;                /* just for sanity checks only*/
	unsigned b_static:1;                /* 1=static keyword found     */
	unsigned b_extern:1;                /* 1=extern keyword found     */
    unsigned b_volatile:1;              /* is marked as volatile      */
	unsigned b_const:1;                 /* is a constant              */
	unsigned b_typedef:1;               /* is typedefed               */
	unsigned b_isenum:1;                /* is an enumerated type      */
    unsigned _stack;                    /* stack offset for stacked v */

	union
	  {                                 /* Values if constant or enum */
		TYPE_TARGET_INT   v_int;        /* 2 bytes: int and char values           */
		char             *v_char;       /*          character string              */
		TYPE_TARGET_UINT  v_uint;       /* 2 bytes: unsigned int const value      */
		TYPE_TARGET_LONG  v_long;       /* 4 bytes: long constant value           */
		TYPE_TARGET_ULONG v_ulong;      /* 4 bytes: unsigned long constant value  */
		struct symbol    *v_enum;       /* ptr to enum_list if enum == 1          */
	  }
	const_val;

	struct structdef *v_struct;         /* structure pointer      */
} specifier;

/* declarator type */
typedef enum
{
    POINTER = 1,                        /* pointer to near data */
    ARRAY,
    FUNCTION
} DECLARATOR_TYPE;

/* declarator */
typedef struct declarator
{
	DECLARATOR_TYPE dcl_type;           /* POINTER, ARRAY or FUNCTION  */
	unsigned int num_elem;              /* # of elems if type==array, */
										/* always 0 for flexible arrays */
	unsigned ptr_const:1;               /* pointer is constant        */
	unsigned ptr_volatile:1;            /* pointer is volatile        */
	unsigned ptr_restrict:1;            /* pointer is resticted       */
	unsigned is_func_dcl:1;             /* is function declared       */
	struct sym_link *tspec;             /* pointer type specifier     */
} declarator;

typedef enum {
	DECLARATOR = 1,
	SPECIFIER
} SYM_LINK_CLASS;

#define DECLSPEC2TXT(select) (select==DECLARATOR?"DECLARATOR":select==SPECIFIER?"SPECIFIER":"UNKNOWN")

/* symlink */
typedef struct sym_link
{
	SYM_LINK_CLASS _class;              /* DECLARATOR or SPECIFIER    */
	unsigned tdef:1;                    /* current link created by    */
										/* typedef if this flag is set */
	union
	{
		specifier s;                    /* if CLASS == SPECIFIER      */
		declarator d;                   /* if CLASS == DECLARATOR     */
	} select;

    /* function attributes */
    struct {
      struct value *args;               /* the defined arguments      */
      unsigned hasVargs:1;              /* functions has varargs      */
    } funcAttrs;

	struct sym_link *next;              /* next element on the chain  */
} sym_link;

/* symbol */
typedef struct symbol
{
    char name[MAX_SYMBOL_NAME + 1];    /* Input Variable Name     */
    char rname[MAX_NAME + 1];      /* internal name           */

    short level;                        /* declaration lev, fld offset */
    short block;                        /* sequential block # of definition */
    int key;							/* key in hash table */
    unsigned isitmp:1;                  /* is an intermediate temp */
    unsigned _isparm:1;                 /* is a parameter          */
    unsigned cdef:1;                    /* compiler defined symbol */
    unsigned isptr:1;                   /* is a pointer */
    unsigned islocal:1;                 /* is a local variable        */
    unsigned onStack:1;                 /* this symbol allocated on the stack */

    int allocreq ;                      /* allocation is required for this variable */
	unsigned offset;                    /* offset from top if struct */
    int stack;                          /* offset on stack      */

    int lineDef;                        /* defined line number        */
    char *fileDef;                      /* defined filename           */
    int lastLine;                       /* for functions the last line */
    struct sym_link *type;              /* 1st link to declarator chain */
    struct sym_link *etype;             /* last link to declarator chain */
    struct symbol *localof;             /* local variable of which function */
    struct symbol *next;                /* crosslink to next symbol   */
} symbol;

#endif

/* if this link is not null */
extern sym_link *validateLink(sym_link  *l,
                               const char       *macro,
                               const char       *args,
                               const char       select,
                               const char       *file,
                               unsigned         line);


#define SPEC_SCLS(x) validateLink(x, "SPEC_SCLS", #x, SPECIFIER, __FILE__, __LINE__)->select.s.sclass
#define SPEC_NOUN(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.noun
#define SPEC_LONG(x) validateLink(x, "SPEC_LONG", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_long
#define SPEC_SHORT(x) validateLink(x, "SPEC_LONG", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_short
#define SPEC_USIGN(x) validateLink(x, "SPEC_USIGN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_unsigned
#define SPEC_STAT(x) validateLink(x, "SPEC_STAT", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_static
#define SPEC_EXTR(x) validateLink(x, "SPEC_EXTR", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_extern
#define SPEC_VOLATILE(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_volatile
#define SPEC_CONST(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_const
#define SPEC_TYPEDEF(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_typedef
#define SPEC_ENUM(x) validateLink(x, "SPEC_ENUM", #x, SPECIFIER, __FILE__, __LINE__)->select.s.b_isenum
#define SPEC_CVAL(x) validateLink(x, "SPEC_CVAL", #x, SPECIFIER, __FILE__, __LINE__)->select.s.const_val
#define SPEC_STRUCT(x) validateLink(x, "SPEC_NOUN", #x, SPECIFIER, __FILE__, __LINE__)->select.s.v_struct
#define SPEC_STAK(x) validateLink(x, "SPEC_STAK", #x, SPECIFIER, __FILE__, __LINE__)->select.s._stack
#define SPEC_OCLS(x) validateLink(x, "SPEC_OCLS", #x, SPECIFIER, __FILE__, __LINE__)->select.s.oclass

#define DCL_TYPE(l)  validateLink(l, "DCL_TYPE", #l, DECLARATOR, __FILE__, __LINE__)->select.d.dcl_type
#define DCL_ELEM(l)  validateLink(l, "DCL_ELEM", #l, DECLARATOR, __FILE__, __LINE__)->select.d.num_elem
#define DCL_PTR_CONST(l) validateLink(l, "DCL_PTR_CONST", #l, DECLARATOR, __FILE__, __LINE__)->select.d.ptr_const
#define DCL_PTR_VOLATILE(l) validateLink(l, "DCL_PTR_VOLATILE", #l, DECLARATOR, __FILE__, __LINE__)->select.d.ptr_volatile
#define DCL_PTR_RESTRICT(l) validateLink(l, "DCL_PTR_RESTRICT", #l, DECLARATOR, __FILE__, __LINE__)->select.d.ptr_restrict
#define DCL_TSPEC(l) validateLink(l, "DCL_TSPEC", #l, DECLARATOR, __FILE__, __LINE__)->select.d.tspec
#define DCL_FUNC_DCLED(l)  validateLink(l, "DCL_TYPE", #l, DECLARATOR, __FILE__, __LINE__)->select.d.is_func_dcl

#define FUNC_ARGS(x) (x->funcAttrs.args)
#define FUNC_HASVARARGS(x) (x->funcAttrs.hasVargs)

/* type check macros */
#define IS_DECL(x)       ( x && x->_class == DECLARATOR )
#define IS_SPEC(x)       ( x && x->_class == SPECIFIER  )
#define IS_ARRAY(x)      (IS_DECL(x) && DCL_TYPE(x) == ARRAY)
#define IS_DATA_PTR(x)   (IS_DECL(x) && DCL_TYPE(x) == POINTER)
#define IS_PTR(x)        (IS_DECL(x) && (DCL_TYPE(x) == POINTER))
#define IS_PTR_CONST(x)  (IS_PTR(x) && DCL_PTR_CONST(x))
#define IS_PTR_RESTRICT(x) (IS_PTR(x) && DCL_PTR_RESTRICT(x))
#define IS_FUNCPTR(x)    (IS_DECL(x) && IS_FUNC(x->next))
#define IS_FUNC(x)       (IS_DECL(x) && DCL_TYPE(x) == FUNCTION)
#define IS_LONG(x)       (IS_SPEC(x) && x->select.s.b_long)
#define IS_UNSIGNED(x)   (IS_SPEC(x) && x->select.s.b_unsigned)
#define IS_TYPEDEF(x)    (IS_SPEC(x) && x->select.s.b_typedef)
#define IS_STRUCT(x)     (IS_SPEC(x) && x->select.s.noun == V_STRUCT)
#define IS_REGISTER(x)   (IS_SPEC(x) && SPEC_SCLS(x) == S_REGISTER)
#define IS_STATIC(x)     (IS_SPEC(x) && SPEC_STAT(x))
#define IS_INT(x)        (IS_SPEC(x) && x->select.s.noun == V_INT)
#define IS_VOID(x)       (IS_SPEC(x) && x->select.s.noun == V_VOID)
#define IS_CHAR(x)       (IS_SPEC(x) && x->select.s.noun == V_CHAR)
#define IS_EXTERN(x)     (IS_SPEC(x) && x->select.s.b_extern)
#define IS_AGGREGATE(x)  (IS_ARRAY(x) || IS_STRUCT(x))
#define IS_LITERAL(x)    (IS_SPEC(x)  && x->select.s.sclass == S_LITERAL)
#define IS_VOLATILE(x)  (!x ? 0 : \
                           IS_SPEC(x) ? \
                           x->select.s.b_volatile : \
                           x->select.d.ptr_volatile)
#define IS_INTEGRAL(x)   (IS_SPEC(x) && (x->select.s.noun == V_INT ||  \
                                         x->select.s.noun == V_CHAR))
#define IS_ARITHMETIC(x) (IS_INTEGRAL(x))
#define IS_ARGUMENT(x)	 (x && x->localof != NULL)

extern symbol *currFunc;

extern bucket *SymbolTab[];
extern bucket *StructTab[];
extern bucket *TypedefTab[];


/************************** 
* symbol table management *
**************************/

/* create symbol table and element */
void initSymt ();
bucket *newBucket ();
sym_link * newLongLink ();
sym_link * newIntLink ();
sym_link * newCharLink ();
symbol *newSymbol (char *, int);
sym_link *newLink (SYM_LINK_CLASS);
structdef *newStruct (char *);
void addDecl (symbol *, int, sym_link *);
void processFuncArgs (symbol * );
sym_link *mergeSpec (sym_link *, sym_link *, char *name);
symbol *reverseSyms (symbol *);
sym_link *reverseLink (sym_link *);
symbol *copySymbol (symbol *);
symbol *copySymbolChain (symbol *);
char *genSymName (int);
sym_link *getSpec (sym_link *);
sym_link *copyLinkChain (sym_link *);
int checkDecl (symbol *, int);
char *printLinkChain(sym_link *sl);

/* adding and removing */
void addSymChain (symbol **);
void addSym (bucket **, void *, char *, int, int, int checkType);
void deleteSym (bucket **, void *, char *);
void *findSym (bucket **, void *, const char *);
void *findSymWithLevel (bucket **, struct symbol *);
void *findSymWithBlock (bucket **, struct symbol *, int);
unsigned int getSize (sym_link * );
int checkDecl (symbol * , int );
sym_link *getSpec (sym_link *);
unsigned int bitsForType (sym_link *);
int compareType (sym_link *, sym_link *);
int comparePtrType (sym_link *, sym_link *, bool);
int compareTypeExact (sym_link *, sym_link *, int);
void cleanUpLevel (bucket **, int);
void cleanUpBlock (bucket **, int);
void promoteAnonStructs (int, structdef *);
int compStructSize (int, structdef *);
void checkTypeSanity(sym_link *, char *);
symbol *createFunction(symbol *sym);
symbol *getStructElement (structdef * sdef, symbol * sym);
char *nounName(sym_link *sl);
symbol *resolveArgSymbols (symbol *sym, symbol *func);

/* added by nttrung
 * for debug */
/* view table */
void viewTable(FILE *, bucket **, int, int);